package com.tansun.tandata.service.impl;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.tansun.tandata.common.TandataServerEnum;
import com.tansun.tandata.common.TiShuServerEnum;
import com.tansun.tandata.entity.LogInfoErro;
import com.tansun.tandata.entity.RaiseNum;
import com.tansun.tandata.entity.RaiseNumReceiveUser;
import com.tansun.tandata.feign.AuthFeignService;
import com.tansun.tandata.feign.SysFeignService;
import com.tansun.tandata.mapper.RaiseNumDao;
import com.tansun.tandata.mapper.RaiseNumReceiveUserDao;
import com.tansun.tandata.service.BiServicer;
import com.tansun.tandata.service.RaisNumService;
import com.tansun.tandata.utils.*;
import com.tansun.tandata.vo.LdapUser;
import com.tansun.tandata.vo.LogInfoVO;
import com.tansun.tandata.vo.req.RaiseNumPageVO;
import com.tansun.tandata.vo.req.RaiseNumVO;
import com.tansun.tandata.vo.req.bi.CarrierTableUserVo;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.util.*;

/**
 * @Description 提数管理业务类

 * @CreateDate 20200907
 * @author liuyu
 * @Version v1.0
 *
 */
@Service("raisNumServiceImpl")
public class RaisNumServiceImpl extends ParentServiceImp<RaiseNumDao, RaiseNum>
        implements RaisNumService
{
    private org.slf4j.Logger logger= LoggerFactory.getLogger(this.getClass());

    @Value("${spring.profiles.active}")
    private String active;
    @Value("${raisnum.shellpath}")
    private String shellPath;
    @Value("${raisnum.baseUrl}")
    private String raisBaseUrl;
    @Value("${funyuanbi.connectionName}")
    private String connectionName;
    @Autowired
    private RaiseNumDao raiseNumDao;
    @Autowired
    private RaiseNumReceiveUserDao raiseNumReceiveUserDao;
    @Autowired
    private BiServicer biServicer;
    @Autowired
    private SysFeignService sysFeignService;
    @Autowired
    private AuthFeignService authFeignService;
    /**
     * @Description 添加提数记录

     * @CreateDate 20200907
     * @author liuyu
     * @Version v1.0
     *
     */
    @Transactional(propagation= Propagation.REQUIRED, rollbackFor=Exception.class)
    public Integer addRaisNum(RaiseNumVO vo)
    {
        int result;
        RaiseNum obj= null;
        List<RaiseNumReceiveUser> userList = null;
        HashMap<String,Object> map = saveOrUpdateRaise(vo,obj,userList);
        result = (int) map.get("code");
        if(result!=1){
            return result;
        }
        obj = map.get("obj")==null?null:(RaiseNum) map.get("obj");
        userList= map.get("userList")==null?null:(List<RaiseNumReceiveUser>) map.get("userList");
        if (userList != null) {
            //批量保存提数记录于域用户之间的关系
            raiseNumReceiveUserDao.insertBath(userList);
        }
        //报存提数记录
        raiseNumDao.insert(obj);
        /**保存相关日志开始TODO*/
        //TODO
        /**保存相关日志结束TODO*/
        return 1;
    }
    /**
     * @Description 编辑提数记录

     * @CreateDate 20200907
     * @author liuyu
     * @Version v1.0
     *
     */
    @Override
    @Transactional(propagation= Propagation.REQUIRED, rollbackFor=Exception.class)
    public Integer upDateRaisNum(RaiseNumVO vo) {
        int result;
        RaiseNum obj= null;
        List<RaiseNumReceiveUser> userList = null;
        HashMap<String,Object> map = saveOrUpdateRaise(vo,obj,userList);
        result = (int) map.get("code");
        if(result!=1){
            return result;
        }
        obj = map.get("obj")==null?null:(RaiseNum) map.get("obj");
        userList= map.get("userList")==null?null:(List<RaiseNumReceiveUser>) map.get("userList");
        if (userList != null) {
            //删除旧的提数记录于域用户之间的关系
            raiseNumReceiveUserDao.deleteByRaiseNumId(obj.getId());
            //批量保存提数记录于域用户之间的关系
            raiseNumReceiveUserDao.insertBath(userList);
        }
        //报错提数记录
        raiseNumDao.updateByPrimaryKey(obj);
        /**保存相关日志开始TODO*/
        //TODO
        /**保存相关日志结束TODO*/
        return 1;
    }
    /**
     * @Description 根据条件查询提数记录列表

     * @CreateDate 20200907
     * @author liuyu
     * @Version v1.0
     *
     */
    @Override
    public List<RaiseNum> getRaiseNumList(RaiseNumPageVO raiseNumPageVO) {
        return raiseNumDao.getRaiseNum(raiseNumPageVO);
    }
    /**
     * @Description 根据id删除提数记录

     * @CreateDate 20200907
     * @author liuyu
     * @Version v1.0
     *
     */
    @Override
    public Integer deleteRaisNum(String id) {
        //删除旧的提数记录于域用户之间的关系
        raiseNumReceiveUserDao.deleteByRaiseNumId(id);
        /**保存相关日志开始TODO*/
        //TODO
        /**保存相关日志结束TODO*/
        return raiseNumDao.deleteByPrimaryKey(id);
    }
    /**
     * @Description 根据id逻辑删除提数记录

     * @CreateDate 20200907
     * @author liuyu
     * @Version v1.0
     *
     */
    @Override
    public Integer deleteRaisNum_logic(String id) {
        RaiseNum obj = new  RaiseNum();
        obj.setId(id);
        obj.setState(5);//删除状态
        obj.setIsdele(1);//删除状态
        /**保存相关日志开始TODO*/
        //TODO
        /**保存相关日志结束TODO*/
        return raiseNumDao.updateByPrimaryKeySelective(obj);
    }

    @Override
    public List<RaiseNum> getReciivetBylimit() {
        return raiseNumDao.selectReciivetBylimit();
    }
    /**
     * @Description 调用提数shell脚本

     * @CreateDate 20200907
     * @author liuyu
     * @Version v1.0
     *
     */
    @Override
    public Integer execRaisShell(RaiseNum obj, String shellPath) {
        logger.error("----------进入execRaisShell--------------"+JsonUtil.toJsonObjectString(obj));
        HashMap<String, Object> resultMap = new HashMap<>();
        //发送请求调用提数脚本
        try {
            String result= HttpUtil.doGet(raisBaseUrl+"/execShell/execRaisNumShell?tableName="+obj.getTableName()+"&raisId="+obj.getId());
            logger.error("----------调用提数脚本结束-------------"+result);
            //保存错误日志
            LogInfoErro log = new LogInfoErro();
            log.setServerId(TandataServerEnum.Five.getServerId());//tandata-tishuussiness
            log.setUrl(raisBaseUrl+"/execShell/execRaisNumShell?tableName="+obj.getTableName()+"&raisId="+obj.getId());
            log.setReqType(0);//get
            if(StringUtil.isEmpty(result)){
                //请求没有接收到返回结果
                log.setDesribe("请求没有接收到返回结果");
                logger.error("----------请求没有接收到返回结果-------------");
                sysFeignService.insertLogInfoErro(log);
                return -3;
            }

            JSONObject jsonObject=  JsonUtil.stringToJson(result);
            if(jsonObject!=null){
                int code = jsonObject.getInteger("code");
                if(code!=1){
                    //保存错误日志
                    log.setDesribe("调用执行提数脚本失败，错误详情:"+jsonObject.getString("msg"));
                    logger.error("调用执行提数脚本失败，错误详情:"+jsonObject.getString("msg"));
                    sysFeignService.insertLogInfoErro(log);
                }
                return code;
            }
        }catch (Exception e){
            e.printStackTrace();
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            e.printStackTrace(new PrintStream(baos));
            //保存错误日志
            LogInfoErro log = new LogInfoErro();
            log.setServerId(TandataServerEnum.Five.getServerId());//tandata-tishuussiness
            log.setUrl(raisBaseUrl+"/execShell/execRaisNumShell?tableName="+obj.getTableName()+"&raisId="+obj.getId());
            log.setReqType(0);//get
            log.setDesribe(baos.toString());
            sysFeignService.insertLogInfoErro(log);
            return -999;//发送http请求失败
        }
        return 0;
    }

    @Override
    public Integer execStopRaisShell(String id) {
        logger.error("----------execStopRaisShell--------------"+id);
        //查询提数记录
        RaiseNum obj = raiseNumDao.selectByPrimaryKey(id);
        if(2!=obj.getState()){
            //不是待执行状态的项目直接返回
            return -9;
        }
        HashMap<String, Object> resultMap = new HashMap<>();
        //发送请求调用提数脚本
        try {
            String result= HttpUtil.doGet(raisBaseUrl+"/execShell/execStopRaisNumShell?tableName="+obj.getTableName()+"&raisId="+obj.getId());
            logger.error("----------调用终止提数脚本结束-------------"+result);
            //保存错误日志
            LogInfoErro log = new LogInfoErro();
            log.setServerId(TandataServerEnum.Five.getServerId());//tandata-tishuussiness
            log.setUrl(raisBaseUrl+"/execShell/execStopRaisNumShell?tableName="+obj.getTableName()+"&raisId="+obj.getId());
            log.setReqType(0);//get
            if(StringUtil.isEmpty(result)){
                //请求没有接收到返回结果
                log.setDesribe("请求没有接收到返回结果");
                logger.error("----------请求没有接收到返回结果-------------");
                sysFeignService.insertLogInfoErro(log);
                return -3;
            }

            JSONObject jsonObject=  JsonUtil.stringToJson(result);
            if(jsonObject!=null){
                int code = jsonObject.getInteger("code");
                if(code!=1){
                    //保存错误日志
                    log.setDesribe("调用执行终止提数脚本失败，错误详情:"+jsonObject.getString("msg"));
                    logger.error("调用执行终止提数脚本失败，错误详情:"+jsonObject.getString("msg"));
                    sysFeignService.insertLogInfoErro(log);
                }
                return code;
            }
        }catch (Exception e){
            e.printStackTrace();
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            e.printStackTrace(new PrintStream(baos));
            //保存错误日志
            LogInfoErro log = new LogInfoErro();
            log.setServerId(TandataServerEnum.Five.getServerId());//tandata-tishuussiness
            log.setUrl(raisBaseUrl+"/execShell/execStopRaisNumShell?tableName="+obj.getTableName()+"&raisId="+obj.getId());
            log.setReqType(0);//get
            log.setDesribe(baos.toString());
            sysFeignService.insertLogInfoErro(log);
            return -999;//发送http请求失败
        }
        return 0;
    }

    @Override
    @Transactional(propagation= Propagation.REQUIRED, rollbackFor=Exception.class)
    public Integer execRaisShellBack(String state,String raisNumId,String logId) {
        logger.error("--------------进入回调方法execRaisShellBack-----------raisNumId"+raisNumId+"state:"+state+"logId:"+logId);
        //组装日志对象
        LogInfoVO logInfoVO = new LogInfoVO();
        logInfoVO.setServerId(TandataServerEnum.Five.getServerId());//tandata-tishuussiness
        logInfoVO.setModular(TiShuServerEnum.One.getServerName());//提数管理
        logInfoVO.setBkey(logId);
        //获取提数记录
        RaiseNum obj =raiseNumDao.selectByPrimaryKey(raisNumId);
        if(obj==null){
            return Constants.RAIS_NUM_ERRO_1;//提数记录不存在
        }
        Date now = new Date();
        obj.setEndTime(now);//执行结束时间
        //提数失败的场合
        if(!Constants.HTTP_RES_CODE_200_VALUE.equals(state)){
            obj.setState(4);//执行失败
            raiseNumDao.updateByPrimaryKeySelective(obj);
            //TODO 保存相关日志
            logInfoVO.setOperation("提数操作");
            String message="提数记录"+obj.getTitle()+"提数脚本执行报错！"+"\n"+"提数记录id"+raisNumId;
            logInfoVO.setComment(message);
            logInfoVO.setSuccess(0);
            sysFeignService.insertLogInfo(logInfoVO);
            return -2;//todo根据相关state返回业务不同的业务异常
        }
        //提数成功的场合
        /******同步bi系统开始********/
        //创建bi业务表
        JSONObject biResult = biServicer.addBiTable(obj.getTableName());
        logger.error("--------------调用bi建表结束-----------"+biResult.toJSONString());
        boolean success = biResult.getBoolean("success");
        if(!success){
            //jsd4273_001为在比系统中业务表已经建立，此种情况继续执行后续的授权操作
            if(!"jsd4273_001".equals(biResult.getString("errorCode"))) {
                //TODO 保存相关日志
                String errorCode = biResult.getString("errorCode");
                logInfoVO.setOperation("提数操作");
                String message = "提数记录" + obj.getTitle() + "提数成功但同步帆软BI系统创建基础表失败！" + "\n" + "提数记录id" + raisNumId + "\n错误编码：" + errorCode;
                logInfoVO.setComment(message);
                logInfoVO.setSuccess(0);
                sysFeignService.insertLogInfo(logInfoVO);
                obj.setState(6);//执行提数成功但同步bi系统失败
                raiseNumDao.updateByPrimaryKeySelective(obj);
                return -3;//todo根据相关state返回业务不同的业务异常
            }
        }
        //在bi系统给用户进行授权
        //组装参数
        List<CarrierTableUserVo> paramList = new ArrayList<>();
        List<RaiseNumReceiveUser> users = raiseNumReceiveUserDao.gainListByRaisenumId(obj.getId());
        if(users!=null&&!users.isEmpty()){
            CarrierTableUserVo carrierTableUserVo = null;
            for(RaiseNumReceiveUser user:users){
                carrierTableUserVo = new CarrierTableUserVo();
                carrierTableUserVo.setTableName(connectionName+"_"+obj.getTableName()+"_D");
                carrierTableUserVo.setUserName(user.getUserName());
                paramList.add(carrierTableUserVo);
            }
            //调用bi接口同步权限
            biResult = biServicer.carrierTableUser(paramList);
            logger.error("--------------调用bi同步接口结束-----------"+biResult.toJSONString());
            if(biResult==null||!Constants.HTTP_RES_CODE_200_VALUE.equals(biResult.getString("data"))){
                //TODO 保存相关日志
                String errorCode = biResult.getString("errorCode");
                logInfoVO.setOperation("提数操作");
                String message="提数记录"+obj.getTitle()+"提数成功但同步帆软BI系统给用户授权基础表失败！"+"\n"+"提数记录id"+raisNumId+"\n错误编码："+errorCode;
                logInfoVO.setComment(message);
                logInfoVO.setSuccess(0);
                sysFeignService.insertLogInfo(logInfoVO);
                obj.setState(6);//执行提数成功但同步bi系统失败
                raiseNumDao.updateByPrimaryKeySelective(obj);
                return -4;//todo根据相关state返回业务不同的业务异常
            }
        }
        /******同步bi系统结束********/
        obj.setState(3);//执行成功
        raiseNumDao.updateByPrimaryKeySelective(obj);
        //TODO 保存相关日志
        logInfoVO.setSuccess(1);//提数成功
        logInfoVO.setOperation("提数操作");
        sysFeignService.insertLogInfo(logInfoVO);
        logger.error("---------------回调业务完成--------------"+raisNumId);
        return 1;
    }
    /**
     * @Description 查找域账号用户列表

     * @CreateDate 20200907
     * @author liuyu
     * @Version v1.0
     *
     */
    @Override
    public List<LdapUser> findLdapUserList(String name) {
        List<LdapUser> ldapUsers = null;
        if("dev".equals(active)){
            //开发环境测试数据
            ldapUsers = new ArrayList<>();
            LdapUser user1 = new LdapUser();
            user1.setCn("彭东水");
            user1.setApartment("科技部");
            user1.setSAMAccountName("hsa_user");
            ldapUsers.add(user1);
            LdapUser user2 = new LdapUser();
            user2.setSAMAccountName("login1");
            user2.setCn("系统管理员");
            user2.setApartment("科技部");
            ldapUsers.add(user2);
        }else {
            LdapUser ldapUser = new LdapUser();
            ldapUser.setCn(name);
            ldapUsers =authFeignService.seachLdapUserByKeywords(ldapUser);
        }
        return ldapUsers;
    }

    @Override
    public void execRaisNumShell() {
        //判断Redis是否上锁
        if(redisUtils.get("execRaisNumShell")==null){
            //给redise上锁
            redisUtils.set("execRaisNumShell","execRaisNumShellLock",120);
            Date time = new Date();
            //获取预计提数的列表
            List<RaiseNum> list = raiseNumDao.selectRaisNumByExecShell(time);
            if(list==null||list.size()==0){
                //没有需要提数的项目返回
                //解锁
                redisUtils.delete("execRaisNumShell");
                return;
            }
            //准备提数
            JSONArray array = new JSONArray();
            for (RaiseNum obj:list){
                JSONObject jsonObject = new JSONObject();
                jsonObject.put("tableName",obj.getTableName());
                jsonObject.put("raisId",obj.getId());
                array.add(jsonObject);
            }
            //将状态改为执行中
            raiseNumDao.updateRaisNumStateByTime(time);
            //发送请求提数
            HttpUtil.doPostJson(raisBaseUrl+"/execShell/execRaisNumShellBash",array.toJSONString(),true);
            //解锁
            if(redisUtils.get("execRaisNumShell")!=null) {
                redisUtils.delete("execRaisNumShell");
            }
        }
    }

    @Override
    public void execCleanTableShell() {
        //判断Redis是否上锁
        if(redisUtils.get("execCleanTableShell")==null){
            //给redise上锁
            redisUtils.set("execCleanTableShell","execCleanTableShellLock",120);
            //获取过期需要删掉的基础表
            List<RaiseNum> list = raiseNumDao.selectRaisNumOutSaveDate(new Date());
            if(list==null||list.size()==0){
                //没有需要删除的项目返回
                //解锁
                redisUtils.delete("execCleanTableShell");
                return;
            }
            StringBuffer sb =new StringBuffer();
            for (RaiseNum obj:list){
                sb.append(";"+obj.getTableName());
            }
            //组装请求参数
            JSONObject json = new JSONObject();
            json.put("tableNameStr",sb.substring(1));
            //发送请求提数
            HttpUtil.doPostJson(raisBaseUrl+"/execShell/execDeleteTable",json.toJSONString(),true);
            //解锁
            if(redisUtils.get("execCleanTableShell")!=null) {
                redisUtils.delete("execCleanTableShell");
            }
        }
    }

    @Override
    public Integer updateBeginTime(String id) {
        RaiseNum obj = new RaiseNum();
        obj.setId(id);
        obj.setBeginTime(new Date());
        return raiseNumDao.updateByPrimaryKeySelective(obj);
    }

    /**
     * @Description 保存或更新提数记录核心业务

     * @CreateDate 20200907
     * @author liuyu
     * @Version v1.0
     *
     */
    private HashMap<String,Object> saveOrUpdateRaise(RaiseNumVO vo, RaiseNum obj, List<RaiseNumReceiveUser> userList){
        HashMap<String,Object> resultMap =new HashMap<>();
        obj = gainRaiseNum(vo);
        if(obj==null){
            //更新的情况找不到原提数记录
            resultMap.put("code",-1);
            return resultMap;
        }
        //设置提数记录与域账户的关系
        if(!StringUtil.isEmpty(obj.getReciiveStr())) {
            userList =  new ArrayList<>();
            String[] users = obj.getReciiveStr().split(";");
            String reciiveUsername="";
            String reciiveUserCode = "";
            RaiseNumReceiveUser raiseNumReceiveUser =null;
            for (int i = 0;i<users.length;i++){
                String userStr = users[i];
                if(StringUtil.isEmpty(userStr)){
                    continue;
                }
                raiseNumReceiveUser = new RaiseNumReceiveUser();
                raiseNumReceiveUser.setId(UUID.randomUUID().toString().replaceAll("-", ""));
                raiseNumReceiveUser.setRaisenumId(obj.getId());
                raiseNumReceiveUser.setUserName(userStr.substring(0,userStr.indexOf(",")));
                raiseNumReceiveUser.setUserNickname(userStr.substring(userStr.indexOf(",")+1));
                reciiveUsername+="、"+raiseNumReceiveUser.getUserNickname();
                reciiveUserCode+="、"+raiseNumReceiveUser.getUserName();
                userList.add(raiseNumReceiveUser);
            }
            if(!"".equals(reciiveUserCode)) {
                obj.setRaiseUsercode(reciiveUserCode.substring(1));
            }
            if(!"".equals(reciiveUsername)) {
                obj.setReciiveUsername(reciiveUsername.substring(1));
            }
        }
        if(1==vo.getSubmitFlag()) {
            //提交的场合
            if (0 == vo.getRaiseNow()) {
                //立即提数的场合
                obj.setBeginTime(new Date());
                obj.setState(2);//执行中
                //立即提数的场合
                if(!"dev".equals(active)) {
                    /**调用提数shell开始TODO*/
                    //TODO
                    int code= execRaisShell(obj,shellPath);
                    if(code!=1){
                        resultMap.put("code",code);
                        return resultMap;
                    }

                    /**调用提数shell结束TODO*/
                }
            }else if(new Date().compareTo(obj.getBeginTime())>0){
                //延时提数时预计开始时间超过了当前时间返回相关报错编码
                resultMap.put("code",-2);
                return resultMap;
            }
        }
        resultMap.put("code",1);
        resultMap.put("obj",obj);
        resultMap.put("userList",userList);
        return resultMap;
    }


    /**
     * @Description 组装提数记录

     * @CreateDate 20200907
     * @author liuyu
     * @Version v1.0
     *
     */
    private RaiseNum gainRaiseNum(RaiseNumVO vo) {
        Date now = new Date();
        RaiseNum obj = null;
        if (StringUtil.isEmpty(vo.getId())) {
            obj = new RaiseNum();
            obj.setId(UUID.randomUUID().toString().replaceAll("-", ""));

            String raiseNo = DateUtils.DateToString(now, "yyyyMMdd");
            Integer size = this.raiseNumDao.getTodayItemSize();
            if (size == 0) {
                raiseNo = raiseNo + "00001";
            } else {
                raiseNo = raiseNo + String.format("%05d", size);
            }
            obj.setRaiseNo(raiseNo);
            obj.setCreateTime(now);

        } else {
            obj = this.raiseNumDao.selectByPrimaryKey(vo.getId());
            if (obj == null) {
                return null;
            }
        }
        if (vo.getRaiseNow() == 0) {
            obj.setRaiseType(0);//立即提数
            obj.setBeginTime(null);
        } else {
            obj.setBeginTime(DateUtils.stringToDate(vo.getBeginTime(), DateUtils.DATE_TO_STRING_DETAIAL_PATTERN));
            obj.setRaiseType(1);//延时提数
        }
        obj.setIsdele(Integer.valueOf(0));
        obj.setTitle(vo.getTitle());
        obj.setTableName(vo.getTableName());
        obj.setSqlText(vo.getSqlText());
        obj.setSeeDate(vo.getSeeDate());
        obj.setSaveDate(vo.getSaveDate());
        //设置数据保存时间
        Date createDate = obj.getCreateTime();
        Calendar   calendar = new GregorianCalendar();
        if(vo.getSaveDate()!=null) {
            calendar.setTime(createDate);
            calendar.add(calendar.DATE, vo.getSaveDate()); //把日期往后增加N天,整数  往后推,负数往前移动
            Date saveDate = calendar.getTime(); //这个时间就是日期往后推N天的结果
            obj.setSaveEndTme(saveDate);
        }
        if(vo.getSeeDate()!=null) {
            calendar = new GregorianCalendar();
            calendar.setTime(createDate);
            calendar.add(calendar.DATE, vo.getSeeDate()); //把日期往后增加N天,整数  往后推,负数往前移动
            Date seeDate = calendar.getTime();
            obj.setSeeEndTime(seeDate);
        }
        obj.setReciiveStr(vo.getReciiveStr());
        if(1==vo.getSubmitFlag()){
            //提交的场合
            obj.setState(1);//待执行
        }else {
            //保存的场合
            obj.setState(0);//新建状态
        }
        return obj;
    }
}